import asyncio
import math
import csv
from django.http import StreamingHttpResponse

from io import BytesIO
from django.db.models import Q
from django.http import JsonResponse
from rest_framework import filters
from django.shortcuts import HttpResponse
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import SessionAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework import mixins, viewsets, status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth import get_user_model
from django_filters.rest_framework import DjangoFilterBackend
from .models import GasStationInfo, Orders, ExpiryDatePrice, ReceiptTicket, GasFollow, ApiChannel
from .filters import GasStationInfoFilter, OrdersFilter, AdminOrdersFilter, BaseAdminOrdersFilter, AdminOrdersTotalFilter
from apps.supply_order.utils import UntilPagination
from .serializers import OrdersSerializers, OrdersCancelSerializers, EquityCancelSerializers, OrderRetrieveSerializers, \
    ExpiryDatePriceSerializers, DepreciateAmountSerializers, ReceiptTicketSerializers, OrderTicketSerializers, \
    GasFollowSerializer, OrdersSerializer, APIChannelSerializers, OrdersTotalSerializer
from core.yl.models import YLRefundRecord, YLPayRecord
from core.tl.models import TLRefundRecord, TLPayRecord
from core.wchat.models import RefundRecord as WXRefundRecord
from apps.supply_order.models import RefundRecord
from core.yl.client import YLClient
from core.tools.func_utils import funcUtils
from core.wchat.WXInstance import KLBWxPay as wx_pay
from .utils import distance_sorted, price_sorted_by_custom, in_time_update_oil, get_czb_channel_auth
from .task import czb_refund_callback
from core.wchat.models import WxPayOrder
from .client import get_czb_api, get_czb_api_by_czb_appkey
from core.wchat.wx_base import WxBase
from core.zhongxin.models import OrderRefund, OrderRecord

UserModel = get_user_model()
wx_base = WxBase()

yl_client = YLClient()


# 加油站信息列表
class QueryPriceByPhone(mixins.ListModelMixin, viewsets.GenericViewSet):
    queryset = GasStationInfo.objects.all().prefetch_related("oilinfo_set")
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    pagination_class = UntilPagination
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = GasStationInfoFilter
    queryset_result = None
    max_num = 99

    def initial(self, request, *args, **kwargs):
        super(QueryPriceByPhone, self).initial(request, *args, **kwargs)
        # 获取车主邦提供的对接信息(渠道信息)
        channel_key = self.request.query_params.get("channel") if self.request.query_params.get(
            "channel") else "youpinhuiyuan"
        czb_api = get_czb_api(channel_key)
        setattr(self, "czb_api", czb_api)
        mobile = self.request.query_params.get("mobile")
        try:
            user = UserModel.objects.get(mobile=mobile)  # 拿到手机号
            # 从缓存中获取到车主邦token，完成注册
            content = get_czb_channel_auth(user, czb_api, channel_key=channel_key)  # 如果有错误信息直接展示错误信息
            return Response(content)
        except Exception as e:
            pass

    # 列表
    def list(self, request, *args, **kwargs):
        queryset_result = self.filter_queryset(self.get_queryset())
        queryset_result = queryset_result.exclude(gasName__contains="车主邦")
        oilNo = self.request.query_params.get("oilNo", 92) if self.request.query_params.get("oilNo", 92) else 92
        queryset_gasIds = queryset_result[0:self.max_num].values("gasId")
        gas_ids = ",".join((i.get("gasId") for i in queryset_gasIds))
        # 获取价格
        mobile = self.request.query_params.get("mobile")
        if not mobile:
            mobile = "18612386233"
        response = self.czb_api.query_price_by_phone(gas_ids, mobile)
        result = response.get("result")
        if not result:
            result = []
        # 实时更新数据
        gas_json_list = in_time_update_oil(result, oilNo)
        # 排序
        if self.request.query_params.get("order_by") == 1 or self.request.query_params.get("order_by") == "1":
            gas_json_list = distance_sorted(gas_json_list, self.request.query_params.get("position"))
        elif self.request.query_params.get("order_by") == 2 or self.request.query_params.get("order_by") == "2":
            gas_json_list = price_sorted_by_custom(gas_json_list)
        else:
            gas_json_list = distance_sorted(gas_json_list, self.request.query_params.get("position"))
        return Response(gas_json_list)

    # 异步查询
    def list_async(self, request, *args, **kwargs):
        import datetime
        print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "start")
        oilNo = self.request.query_params.get("oilNo", 92)
        self.queryset_result = self.filter_queryset(self.get_queryset())
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        self.loop = loop
        try:
            response = loop.run_until_complete(self.gather_tasks())
            base_list = []
            # 替换格式
            for i in response:
                for o in i:
                    base_list.append(o)
            request_gas_list = []
            print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "end")
            # 根据过滤出来的信息
            for i in self.queryset_result:  # 在本地数据库里的数据
                data = dict()
                data["gasAddress"] = i.gasAddress
                data["gasLogoBig"] = i.gasLogoBig
                data["gasLogoSmall"] = i.gasLogoSmall
                data["gasAddressLongitude"] = i.gasAddressLongitude
                data["gasAddressLatitude"] = i.gasAddressLatitude
                data["gasId"] = i.gasId
                data["gasName"] = i.gasName
                for one in base_list:  # 请求过来的数据
                    if str(one.get("gasId")) == str(i.gasId):  # 如果是同一个数据
                        oilPriceList = one.get("oilPriceList")
                        for one_oil in oilPriceList:  # 并且请求来的数据里也有同样的油号
                            # 过滤油号，只有存在此油号的加油站才放进去
                            if str(one_oil.get("oilNo")) == str(oilNo):
                                data["priceYfq"] = one_oil.get("priceYfq")
                                data["oilNo"] = one_oil.get("oilNo")
                                data["priceGun"] = one_oil.get("priceGun")
                                data["priceOfficial"] = one_oil.get("priceOfficial")
                                request_gas_list.append(data)  # 才放进去
                                break
            if self.request.query_params.get("order_by") == 1 or self.request.query_params.get("order_by") == "1":
                request_gas_list = distance_sorted(request_gas_list, self.request.query_params.get("position"))
            elif self.request.query_params.get("order_by") == 2 or self.request.query_params.get("order_by") == "2":
                request_gas_list = price_sorted_by_custom(request_gas_list)
            else:
                pass
            print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "end end")
            return Response(request_gas_list)
        finally:
            loop.close()

    def get_gas_ids(self, i):
        start_num = (i - 1) * self.max_num
        end_num = i * self.max_num
        queryset_gasIds = self.queryset_result[start_num:end_num].values("gasId")
        gasIds = ",".join((i.get("gasId") for i in queryset_gasIds))
        return gasIds

    async def gather_tasks(self):
        queryset_count = self.queryset_result.count()
        range_num = math.ceil(queryset_count / float(self.max_num))
        tasks = []
        for i in range(1, range_num + 1):
            ids = self.get_gas_ids(i)
            tasks.append(self.make_future(self.task, ids))
        results = await asyncio.gather(*tasks)
        return results

    async def make_future(self, func, *args):
        # loop.run_in_executor()方法是用来传入需要执行的对象，
        # 以及执行参数，这个方法会维护一个ThreadPoolExecutor()线程池，
        # 如果我们第一个参数是None，run_in_executor()就会把我们的执行对象和参数提交给背后维护的ThreadPoolExecutor()执行，
        # 如果我们传入自己定义的一个线程池，则把执行对象和参数传给我们定义的线程池执行
        future = self.loop.run_in_executor(None, func, *args)
        await future
        response = await future
        return response

    # 发送获取列表任务
    def task(self, gas_ids):
        response = self.czb_api.query_price_by_phone(gas_ids, self.request.user.mobile)
        return response.get("result")


# 石油属性
class OilAttr(APIView):
    def get(self, request):
        response = {
            "type": {
                "gasoline": [
                    {"key": "92#", "value": "92"},
                    {"key": "90#", "value": "90"},
                    {"key": "95#", "value": "95"},
                    {"key": "98#", "value": "98"},
                    {"key": "101#", "value": "101"},
                ],
                "diesel_oli": [
                    {"key": "-40#", "value": "-40"},
                    {"key": "-35#", "value": "-35"},
                    {"key": "-30#", "value": "-30"},
                    {"key": "-20#", "value": "-20"},
                    {"key": "-10#", "value": "-10"},
                    {"key": "国四0#", "value": "-1"},
                    {"key": "0#", "value": "0"},
                ],
                "gas": [
                    {"key": "CNG", "value": "15"},
                    {"key": "LNG", "value": "16"},
                ],
            },
            "brand": [
                {"key": "全部", "value": ""},
                {"key": "中国石油", "value": "1"},
                {"key": "中国石化", "value": "2"},
                {"key": "壳牌", "value": "3"},
                {"key": "其他", "value": "4"},
            ]
        }
        return Response(response)


# 加油站详情
class GasDetail(APIView):
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    permission_classes = (IsAuthenticated,)
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = GasStationInfoFilter

    def get(self, request, gas_id):
        user = self.request.user
        gas_obj = GasStationInfo.objects.get(gasId=gas_id)
        # 获取车主邦提供的对接信息(渠道信息)
        channel_key = self.request.query_params.get("channel") if self.request.query_params.get(
            "channel") else "youpinhuiyuan"
        czb_api = get_czb_api(channel_key)
        response = czb_api.query_price_by_phone(gas_id, self.request.user.mobile)
        content = response.get("result")[0]
        oilPriceList = content.get("oilPriceList")
        # oilPriceList = OilInfo.objects.update_oil_data(gas_obj, content) # 不更新数据库了
        content["id"] = gas_obj.id
        content["gasAddress"] = gas_obj.gasAddress
        content["gasAddressLatitude"] = gas_obj.gasAddressLatitude
        content["gasAddressLongitude"] = gas_obj.gasAddressLongitude
        content["gasLogoSmall"] = gas_obj.gasLogoSmall
        content["gasLogoBig"] = gas_obj.gasLogoBig
        content["oilPriceList"] = oilPriceList
        content["isInvoice"] = gas_obj.isInvoice
        content["gasName"] = gas_obj.gasName
        follow_gas = GasFollow.objects.filter(user=user, gas=gas_obj)
        content["follow_gas"] = GasFollowSerializer(follow_gas, many=True).data
        return Response(content)


# 订单查询、提交订单、取消订单
class OrdersViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin,
                    viewsets.GenericViewSet):
    """
    create: 订单创建
    list: 订单查询
    update:订单取消
    """
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    permission_classes = (IsAuthenticated,)
    # queryset = Orders.objects.all().order_by("-id")
    pagination_class = UntilPagination
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = OrdersFilter

    def get_queryset(self):
        queryset = Orders.objects.filter(user=self.request.user).order_by("-id")
        return queryset

    def get_serializer_class(self):
        if self.action == "update":
            return OrdersCancelSerializers
        elif self.action == "retrieve":
            return OrderRetrieveSerializers
        else:
            return OrdersSerializers

    # def initial(self, request, *args, **kwargs):
    #     super(OrdersViewSet, self).initial(request, *args, **kwargs)
    #     user = self.request.user
    #     if hasattr(user, "userprofileextend"):
    #         if user.userprofileextend.czb_auth:
    #             pass
    #         else:
    #             token = czb_api.get_token_from_mc(user.mobile)
    #             if token:
    #                 user.userprofileextend.czb_auth = True
    #                 user.userprofileextend.save()
    #             else:
    #                 content = {"errmsg": "注册失败，请刷新重试"}
    #                 return Response(content, status=status.HTTP_400_BAD_REQUEST)
    #     else:
    #         token = czb_api.get_token_from_mc(user.mobile)
    #         if token:
    #             UserProfileExtend.objects.create(user=user, czb_auth=True)
    #         else:
    #             content = {"errmsg": "注册失败，请刷新重试"}
    #             return Response(content, status=status.HTTP_400_BAD_REQUEST)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        instance = self.perform_create(serializer)
        # headers = self.get_success_headers(serializer.data)
        return Response({"supply_id": instance.id}, status=status.HTTP_201_CREATED)

    def perform_create(self, serializer):
        instance = serializer.save()
        return instance

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}
        if instance.result == 5:
            content = {"message": "退款已申请，请耐心等待"}
            return Response(content)
        elif instance.result == 101:
            content = {"message": "订单已取消"}
            return Response(content)
        else:
            content = {"message": "操作无效请重试"}
            return Response(content, status.HTTP_400_BAD_REQUEST)

            # def retrieve(self, request, *args, **kwargs):
            #     instance = self.get_object()
            #     if instance.pay_type == 6 and instance.result == 0: # 中信支付
            #         out_trade_no = instance.out_trade_no
            #         zx_client = ZXClient()
            #         params, response = zx_client.search_order(out_trade_no)
            #         print(response, params)
            #         instance.update_pay_result(1)  # 1:支付成功
            #         instance.save()
            #         instance = OrderRecord.objects.get_order_record_instance(out_trade_no)
            #         instance.update_search_response(response)
            #         return
            #     serializer = self.get_serializer(instance)
            #     return Response(serializer.data)


# 购买权益
class EquityViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
    """
    create: 订单创建
    list: 订单查询
    update:订单取消
    """
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    permission_classes = (IsAuthenticated,)
    serializer_class = EquityCancelSerializers

    # def initial(self, request, *args, **kwargs):
    #     super(EquityViewSet, self).initial(request, *args, **kwargs)
    #     user = self.request.user
    #     if hasattr(user, "userprofileextend"):
    #         if user.userprofileextend.czb_auth:
    #             pass
    #         else:
    #             token = czb_api.get_token_from_mc(user.mobile)
    #             if token:
    #                 user.userprofileextend.czb_auth = True
    #                 user.userprofileextend.save()
    #             else:
    #                 content = {"errmsg": "注册失败，请刷新重试"}
    #                 return Response(content, status=status.HTTP_400_BAD_REQUEST)
    #     else:
    #         token = czb_api.get_token_from_mc(user.mobile)
    #         if token:
    #             UserProfileExtend.objects.create(user=user, czb_auth=True)
    #         else:
    #             content = {"errmsg": "注册失败，请刷新重试"}
    #             return Response(content, status=status.HTTP_400_BAD_REQUEST)


# 权益期限及价格
class ExpiryDatePriceSerializersViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    权益期限及价格
    list:权益期限及价格
    """
    queryset = ExpiryDatePrice.objects.all().order_by("-id")
    serializer_class = ExpiryDatePriceSerializers


# 查询直降金额
class DepreciateAmountViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = DepreciateAmountSerializers
    permission_classes = (IsAuthenticated,)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        response = self.perform_create(serializer)
        return Response(response, status=status.HTTP_200_OK)

    def perform_create(self, serializer):
        return serializer.save()


# 模拟支付成功
def pay_success(request, id):
    czb_order_instance = Orders.objects.get(orderId=id)
    czb_order_instance.update_pay_result(1)
    return HttpResponse("success")


# 车主邦发送退款请求
def refund_callback(request):
    print(request.POST, "post refund_callback")
    if request.method == "POST":
        app_key = request.POST.get("app_key")
        orderSn = request.POST.get("orderSn")
        outerOrderId = request.POST.get("outerOrderId")
        refundAmount = request.POST.get("refundAmount")
        refundTradeNo = request.POST.get("refundTradeNo")
        sign = request.POST.get("sign")
        timestamp = request.POST.get("timestamp")
        r = {
            "app_key": app_key,
            "orderSn": orderSn,
            "outerOrderId": outerOrderId,
            "refundAmount": refundAmount,
            "sign": sign,
            "timestamp": timestamp,
            "refundTradeNo": refundTradeNo,
        }
        sign = r.pop("sign")
        orders_instance = Orders.objects.get(orderSn=orderSn)
        if orders_instance.result == 2:
            content = {"code": 200, "message": "OK", "result": {}}
            return JsonResponse(content)
        app_key = orders_instance.channel.app_key  # 通联哪个商户收了钱 要从哪个商户退回去
        czb_app_key = orders_instance.czb_app_key
        czb_api = get_czb_api_by_czb_appkey(czb_app_key)
        # czb_api = get_czb_api(app_key)
        sign_v = czb_api.get_sign(r, czb_api.app_secret)
        if sign_v == sign:
            user_id = [
                "ojh4pt-NhPINlEqorner59MUt5kc",  # 刘，
                "ojh4ptynBWEzmimzXxZFGtiOJAsY",  # 李，
                "ojh4pt7nqVr7HbAEQlb1-jAf_WlY",  # 静，
                "ojh4pt2CWsszLCsollqexwxiOAI0",  # 田，
            ]
            out_trade_no = orders_instance.out_trade_no
            # 通联
            if orders_instance.pay_type == 3:
                out_refund_no = funcUtils.unique_value  # 退款订单号
                tl_pay_record_obj = TLPayRecord.objects.get(orderNo=out_trade_no, is_pay=True)
                refundAmount = tl_pay_record_obj.payment
                status, message = TLRefundRecord.objects.tl_refund(refundAmount, out_trade_no, tl_pay_record_obj,
                                                                   out_refund_no,
                                                                   channel_key=app_key)
                url = "https://c2c.kalaibao.com/shop/api/pay/tl/refund_again?order=%s&channel=%s" % (
                    out_trade_no, app_key)
                if not status:
                    content = "通联订单号：out_trade_no：%s\n渠道码：%s\n错误描述：%s\n退款链接：%s" % (out_trade_no, app_key, message, url)
                    for i in user_id:
                        try:
                            wx_base.client.message.send_text(i, content)
                        except Exception as e:
                            print(e)
                status, message = TLRefundRecord.objects.tl_refund_search(refundAmount, out_trade_no, tl_pay_record_obj,
                                                                          channel_key=app_key)
                url = "https://c2c.kalaibao.com/shop/api/pay/tl/search_again?order=%s&channel=%s" % (
                    out_trade_no, app_key)
                if not status:
                    content = "通联订单号：out_trade_no：%s\n渠道码：%s\n错误描述：%s\n退款链接：%s" % (out_trade_no, app_key, message, url)
                    for i in user_id:
                        try:
                            wx_base.client.message.send_text(i, content)
                        except Exception as e:
                            print(e)
            # 微信
            elif orders_instance.pay_type == 1:
                out_refund_no = wx_pay.out_trade_no  # 退款订单号
                wx_pay_record = WxPayOrder.objects.get(out_trade_no=orders_instance.out_trade_no)
                total_fee = wx_pay_record.total_fee
                refundAmount = total_fee
                status, message = WXRefundRecord.objects.wx_refund(total_fee, refundAmount, out_refund_no, out_trade_no)
                if not status:
                    content = "微信订单号：out_trade_no：%s\n错误描述：%s \n微信退款单号:%s" % (
                        out_trade_no, message, out_refund_no)
                    for i in user_id:
                        try:
                            wx_base.client.message.send_text(i, content)
                        except Exception as e:
                            print(e)
            # 4、银联 7、银联支付宝支付
            elif orders_instance.pay_type == 4 or orders_instance.pay_type == 7:
                out_refund_no = yl_client.unique_value
                pay_record_obj = YLPayRecord.objects.get(out_trade_no=out_trade_no, is_pay=True)
                refundAmount = pay_record_obj.totalAmount
                status, message = YLRefundRecord.objects.yl_refund(refundAmount, pay_record_obj.out_trade_no,
                                                                   refundOrderId=out_refund_no,
                                                                   pay_type=orders_instance.pay_type)
                if not status:
                    content = "银联订单号：out_trade_no：%s\n错误描述：%s \n银联退款单号:%s" % (
                        out_trade_no, message, out_refund_no)
                    for i in user_id:
                        try:
                            wx_base.client.message.send_text(i, content)
                        except Exception as e:
                            print(e)
            # api退款
            elif orders_instance.pay_type == 2:
                out_refund_no = refundTradeNo
                r["status"] = 1  # 通知退款成功失败
                czb_refund_callback.delay(orders_instance.id, r)
            # 中信
            elif orders_instance.pay_type == 6:
                zx_order_record = OrderRecord.objects.get_order_record_instance(out_trade_no)
                oldOriginSerialNo = zx_order_record.out_trade_no
                oldOriginTranTime = zx_order_record.requestTimestamp.strftime("%Y-%m-%d %H:%M:%S")
                installment = zx_order_record.installment
                cashAmount = str(zx_order_record.totalAmount)
                zx_refund_instance = OrderRefund.objects.create_refund(oldOriginSerialNo, oldOriginTranTime, cashAmount,
                                                                       installment)
                out_refund_no = zx_refund_instance.out_refund_no
                if zx_refund_instance.errCode != "0000000":
                    content = "中信：out_trade_no：%s\n错误描述：%s \n中信退款单号:%s" % (
                        out_trade_no, zx_refund_instance.errMsg, out_refund_no)
                    for i in user_id:
                        try:
                            wx_base.client.message.send_text(i, content)
                        except Exception as e:
                            print(e)
            else:
                return
            refund_instance = RefundRecord.objects.create_refund(
                refund_fee=refundAmount,
                refund_type=orders_instance.pay_type,
                out_refund_no=out_refund_no
            )
            orders_instance.refund = refund_instance
            orders_instance.result = 2
            orders_instance.save()
            content = {"code": 200, "message": "OK", "result": {}}
            print(content, "车主邦退款异步通知，返回数据")
            return JsonResponse(content)
        else:
            return HttpResponse("ERROR")
    else:
        return HttpResponse("ERROR")


def refund_fail(request):
    if request.method == "POST":
        app_key = request.POST.get("app_key")
        orderSn = request.POST.get("orderSn")
        outerOrderId = request.POST.get("outerOrderId")
        refundTradeNo = request.POST.get("refundTradeNo")
        sign = request.POST.get("sign")
        timestamp = request.POST.get("timestamp")
        r = {
            "app_key": app_key,
            "orderSn": orderSn,
            "outerOrderId": outerOrderId,
            "sign": sign,
            "timestamp": timestamp,
            "refundTradeNo": refundTradeNo,
        }
        sign = r.pop("sign")
        orders_instance = Orders.objects.get(orderSn=orderSn)
        app_key = orders_instance.channel.app_key
        czb_api = get_czb_api(app_key)
        sign_v = czb_api.get_sign(r, czb_api.app_secret)
        if sign_v == sign:
            if orders_instance.pay_type == 2:
                r["status"] = 0
                czb_refund_callback.delay(orders_instance.id, r)
            else:
                orders_instance.result = 6
                orders_instance.save()
            content = {"code": 200, "message": "OK", "result": {}}
            return JsonResponse(content)
    else:
        return HttpResponse("ERROR")


# 发票信息
class ReceiptTicketViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin,
                           viewsets.GenericViewSet):
    serializer_class = ReceiptTicketSerializers
    permission_classes = (IsAuthenticated,)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    lookup_field = "user_id"

    def get_queryset(self):
        return ReceiptTicket.objects.filter(status=True, default=True, user=self.request.user)


# 订单开票
class OrderTicketViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = OrderTicketSerializers
    permission_classes = (IsAuthenticated,)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)


# 关注加油站
class GasFollowViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet, mixins.DestroyModelMixin):
    serializer_class = GasFollowSerializer
    permission_classes = (IsAuthenticated,)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

    def get_queryset(self):
        return GasFollow.objects.filter(user=self.request.user)


class Echo:
    """An object that implements just the write method of the file-like
    interface.
    """

    def write(self, value):
        """Write the value by returning it, instead of storing in a buffer."""
        return value.encode('gb2312')


# 导出订单
class ExportOrders(mixins.ListModelMixin, viewsets.GenericViewSet):
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = AdminOrdersFilter
    serializer_class = OrdersSerializer
    queryset = Orders.objects.select_related("channel").select_related("refund").filter(pay_time__isnull=False).all()

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        serializer = self.get_serializer(queryset, many=True)
        rows = self.write_body(serializer.data)
        pseudo_buffer = Echo()
        writer = csv.writer(pseudo_buffer)

        response = StreamingHttpResponse((writer.writerow(row) for row in rows),
                                         content_type="text/csv")
        response['Content-Disposition'] = 'attachment; filename="czb_orders.csv"'
        return response

    @classmethod
    def write_body(cls, data):
        yield ["渠道名称", "订单号", "订单时间", "退款时间", "订单状态", "支付类型", "车主邦费用", "用户实付费用", "通道费", "服务费"]
        for i in data:
            yield [
                i.get("channel_name"),
                i.get("out_trade_no") + "\t",
                i.get("order_time"),
                i.get("refund_time"),
                i.get("result_desc"),
                i.get("desc_pay_type"),
                i.get("payment_czb"),
                i.get("payment"),
                i.get("bank_server_fee_value"),
                i.get("server_fee")
            ]


# 订单列表
class AdminOrders(mixins.ListModelMixin, viewsets.GenericViewSet):
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = AdminOrdersFilter
    serializer_class = OrdersSerializer
    pagination_class = UntilPagination
    queryset = Orders.objects.select_related("channel").select_related("refund").filter(pay_time__isnull=False).all()


# 订单数据统计
class AdminOrdersTotal(mixins.ListModelMixin, viewsets.GenericViewSet):
    filter_backends = (DjangoFilterBackend, filters.SearchFilter)
    filter_class = AdminOrdersTotalFilter
    serializer_class = OrdersTotalSerializer
    # 支付成功的queryset
    queryset = Orders.objects.filter(pay_time__isnull=False).exclude(result__in=[0, 101]).all()

    def list(self, request, *args, **kwargs):
        base_queryset = self.filter_queryset(self.get_queryset())
        start_time = self.request.query_params.get("start_time")
        end_time = self.request.query_params.get("end_time")
        refund_queryset = payment_queryset = base_queryset
        if start_time:
            payment_queryset = payment_queryset.filter(Q(create_time__gte=start_time))
            refund_queryset = refund_queryset.filter(Q(refund__create_time__gte=start_time))
        if end_time:
            payment_queryset = payment_queryset.filter(Q(create_time__lt=end_time))
            refund_queryset = refund_queryset.filter(Q(refund__create_time__lt=end_time))
        total_data = Orders.objects.admin_total_data(payment_queryset)
        refund_payment_czb = Orders.objects.refund_total_data(refund_queryset)
        total_data["payment_czb"] = total_data["payment_czb"] - refund_payment_czb
        return Response(total_data)


# 渠道列表
class ChannelViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    serializer_class = APIChannelSerializers
    queryset = ApiChannel.objects.all()


# 获取支付方式
class ApiDetailViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = "app_key"
    queryset = ApiChannel.objects.filter(status=True)
    serializer_class = APIChannelSerializers
